#ifndef NETWORK_STREAM_H__
#define NETWORK_STREAM_H__

#include <assert.h>

namespace engine
{
	class NetworkStream
	{
	public:
		inline void setData(void* data, uint16 size)
		{
			mBuffer = (uint8*)data;
			mSize = size;
			mPosition = 0;
		}

		inline void* getData() const
		{
			return mBuffer;
		}

		inline uint16 getLength() const
		{
			return mSize;
		}

		inline void setPosition(uint16 pos)
		{
			assert(pos <= mSize);

			if (pos > mSize)
				pos = mSize;

			mPosition = pos;
		}

		inline uint16 getPosition()
		{
			return mPosition;
		}

		inline uint16 bytesAvaliable() const
		{
			return mSize - mPosition;	
		}

		inline void clear()
		{
			mBuffer = mStackBuffer;
			mSize = 0;
			mPosition = 0;
		}

	public:
		inline bool readBytes(void *data, uint16 size)
		{
			if ((mPosition + size) <= mSize)
			{
				memcpy(data, mBuffer + mPosition, size);
				mPosition += size;
				
				return true;
			}
			else 
			{
				assert(false);
				
				return false;
			}
		}

		inline bool readBool()
		{
			return read<bool>();
		}

		inline uint8 readUInt8()
		{
			return read<uint8>();
		}

		inline uint16 readUInt16()
		{
			return read<uint16>();
		}

		inline uint32 readUInt32()
		{
			return read<uint32>();
		}

		inline uint64 readUInt64()
		{
			return read<uint64>();
		}

		inline int8 readInt8()
		{
			return read<int8>();
		}

		inline int16 readInt16()
		{
			return read<int16>();
		}

		inline int32 readInt32()
		{
			return read<int32>();
		}

		inline int64 readInt64()
		{
			return read<int64>();
		}

		inline String readString()
		{
			uint16 size = readUInt16();

			if (size == 0)
			{
				return String();
			}
			else
			{
				char buff[0xFFFF];
				if (!readBytes(buff, size))
				{
					return String();
				}
				else
				{
					return String(buff, size);
				}
			}
		}

	public:
		inline bool writeBytes(void *data, uint16 size)
		{
			if (mBuffer != mStackBuffer)
			{
				assert(false);

				return false;
			}

			if (mPosition + size > 0xFFFF)
			{
				assert(false);

				return false;
			}

			memcpy(mBuffer + mPosition, data, size);

			mPosition += size;

			if (mSize < mPosition)
				mSize = mPosition;

			return true;
		}

		inline bool writeBool(bool v)
		{
			return write(v);
		}

		inline bool writeUInt8(uint8 v)
		{
			return write(v);
		}

		inline bool writeUInt16(uint16 v)
		{
			return write(v);
		}

		inline bool writeUInt32(uint32 v)
		{
			return write(v);
		}

		inline bool writeUInt64(uint64 v)
		{
			return write(v);
		}

		inline bool writeInt8(int8 v)
		{
			return write(v);
		}

		inline bool writeInt16(int16 v)
		{
			return write(v);
		}

		inline bool writeInt32(int32 v)
		{
			return write(v);
		}

		inline bool writeInt64(int64 v)
		{
			return write(v);
		}

		inline bool writeString(const String& v)
		{
			size_t size = v.length();

			if (size + sizeof(uint16) > 0xFFFF)
			{
				return false;
			}

			if (!writeUInt16((uint16)size))
			{
				return false;
			}

			if (size > 0)
			{
				if (!writeBytes((void*)v.data(), (uint16)size))
				{
					return false;
				}
			}

			return true;
		}

	private:
		template <class T> T read()
		{
			T v;

			bool succ = readBytes(&v, sizeof(T));

			assert(succ);

			return v;
		}

		template <class T> bool write(T v)
		{
			return writeBytes(&v, sizeof(T));
		}

	private:
		uint8 mStackBuffer[0xFFFF];
		uint8* mBuffer;

		uint16 mSize;
		uint16 mPosition;
	};
}


#endif
